/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

package br.ufrj.dcc.comp2.projeto.controles;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import br.ufrj.dcc.comp2.projeto.modelos.Alienigena;
import br.ufrj.dcc.comp2.projeto.modelos.Componentes;
import br.ufrj.dcc.comp2.projeto.modelos.Nave;
import br.ufrj.dcc.comp2.projeto.modelos.Tiro;
import br.ufrj.dcc.comp2.projeto.modelos.Vetor2D;

/**
 * Thread responsavel por controlar a posi��o dos elementos (aliens, tiros, jogador) durante o jogo.
 * 
 * @author Bruno(Magoo), Eluan(Acre), Diego(Silvio), Davi.
 *
 */
public class ControlaComponentes extends Thread implements KeyListener{
	
	private Nave nave;
	private boolean tiroPressionado;
	private long tiroTempo;
	private Vetor2D naveDirecao;
	private List<Tiro> tiros;
	private List<Alienigena> alienigenas;
	private List<Tiro> alienigenasTiros;
	private long tempoInicial; /* do sistema */
	private long tempoPassado; /* desde que a instância foi criada */
	private long tempoUltimoFrame; /* desde que a instância foi criada */
	private MarteAtaca ma;
	private boolean stopThread = false;
		
	public ControlaComponentes(MarteAtaca ma) {
		this.ma = ma;
		this.nave = ma.getControlaJogo().getNave();
		this.tiros = ma.getControlaJogo().getTiros();
		this.alienigenas = ma.getControlaJogo().getAlienigenas();
		this.alienigenasTiros = ma.getControlaJogo().getAlienigenasTiros();
		this.naveDirecao = new Vetor2D();
		this.tiroTempo = 0;
		this.tiroPressionado = false;
	}
	
	/**
	 * N�o permite que o componente x saia da tela na horizontal.
	 * 
	 * @param x Componente a ser avaliado. 
	 */
	public void wrapX(Componentes x) {
		if (x.getPosicao().getX() < 0) {
			x.getPosicao().setX(0);
			x.getVelocidade().setX(0);
		}
		if (x.getPosicao().getX() >  ma.getControlaJogo().getTamanhoTela().getX() - x.getTamanho().getX()) {
			x.getPosicao().setX(ma.getControlaJogo().getTamanhoTela().getX() - x.getTamanho().getX());
			x.getVelocidade().setX(0);
		}
	}
	
	/**
	 * N�o permite que o componente x saia da tela na vertical.
	 * 
	 * @param x Componente a ser avaliado. 
	 */	
	public void wrapY(Componentes x) {
		if (x.getPosicao().getY() < 0) {
			x.getPosicao().setY(0);
			x.getVelocidade().setY(0);
		}
		/* TODO: *2? */
		if (x.getPosicao().getY() > ma.getControlaJogo().getTamanhoTela().getY() - x.getTamanho().getY()*2) {
			x.getPosicao().setY(ma.getControlaJogo().getTamanhoTela().getY() - x.getTamanho().getY()*2);
			x.getVelocidade().setY(0);
		}
	}
	
	/**
	 * Atualiza posi��o dos aliens.
	 *
	 */
	public void movimentaAlienigenas(){
		/* se fizer ali dentro, dá exception */
		List<Alienigena> alienigenasADeletar;
		
		alienigenasADeletar = Collections.synchronizedList(new ArrayList<Alienigena>());
		
		for (Alienigena x : alienigenas){
			if (x.subtractTimeToDie(tempoUltimoFrame)) {
				alienigenasADeletar.add(x);
				continue;
			}
				
			if(x.getTimeToDie() > 0) x.updateKaboom();
			
			if (Math.random() < 0.5)
				x.applyForce(new Vetor2D(0.5f + (float)Math.random(), 0));
			else
				x.applyForce(new Vetor2D(-0.5f - (float)Math.random(), 0));
			
			if (x.getPosicao().getY() > 0) {
				if (Math.random() < 0.5)
					x.applyForce(new Vetor2D(0, (float)Math.random()));
				else
					x.applyForce(new Vetor2D(0, -(float)Math.random()/2));				
			}
			x.updatePhysics();
			
			wrapX(x);
			
			if (x.getTimeToDie() == 0) {
				if (Math.random() < 0.005) {
					ma.getCriaComponentes().criaAlienigenaTiro(x);
				}
			}
		}
		
		synchronized(ma.getControlaJogo().getAlienigenas()) {
			for (Alienigena x : alienigenasADeletar) {
				ma.getControlaJogo().getAlienigenas().remove(x);
			}
		}
	}
	
	/**
	 * Atualiza posi��o dos tiros do jogador.
	 *
	 */
	public void movimentaTiro(){
	
		for (Tiro x : tiros){
			x.updatePhysics();
		}
	}
	
	/**
	 * Atualiza posi��o dos tiros dos aliens.
	 *
	 */
	public void movimentaAlienigenasTiros(){
		
		for (Tiro x : alienigenasTiros){
			x.updatePhysics();
		}
	}

	/**
	 * Movimenta a nave do jogador.
	 *
	 */
	public void movimentaNave(){
		/* TODO: hardcoded */
		nave.applyForce(new Vetor2D(naveDirecao.getX() * 5, naveDirecao.getY() * 5));
		nave.updatePhysics();
		
		this.wrapX(nave);
		this.wrapY(nave);
	}
	
	/**
	 * Thread que controla a posi��o dos componentes do jogo.
	 */
	public void run(){
		tempoInicial = System.currentTimeMillis();
		tempoPassado = 0;
		tempoUltimoFrame = 0;
		while(!stopThread){
			tempoUltimoFrame = System.currentTimeMillis() - tempoPassado - tempoInicial;
			tempoPassado += tempoUltimoFrame;
			
			if (!ma.getControlaJogo().isPausado()) {
				if (this.tiroTempo > 0) {
					this.tiroTempo -= tempoUltimoFrame;
				}
				if (this.tiroPressionado) {
					if (this.tiroTempo <= 0) {
						ma.getCriaComponentes().criaTiro();
						this.tiroTempo = 300;
					}
				}
				
				this.movimentaTiro();
				this.movimentaAlienigenas();
				this.movimentaAlienigenasTiros();
				this.movimentaNave();
				
				ma.getDetectaColisao().detectaTiro();
				ma.getDetectaColisao().detectaAlienigenas();
				ma.getDetectaColisao().detectaAlienigenasTiros();
				ma.getDetectaColisao().colidir();
				
				if (alienigenas.size() < ma.getControlaJogo().getMaxAlienigenas())
					this.ma.getCriaComponentes().criaAlienigenas();
			}
			
			try {
				if(tempoUltimoFrame < 10) {
					Thread.sleep(10 - tempoUltimoFrame);	
				}
			} catch (Exception e) {
				System.out.println("Fudeu aqui? =X");
				// TODO: handle exception
			}
		}
	}
	
	/**
	 * Captura teclas pressionadas.
	 */
	public void keyPressed(KeyEvent evt) {
		if(evt.getKeyCode() == KeyEvent.VK_CONTROL) {
			this.tiroPressionado = true;
		}
		if(evt.getKeyCode() == KeyEvent.VK_LEFT) {
			naveDirecao.setX(-1);
		}
        
		if(evt.getKeyCode() == KeyEvent.VK_RIGHT) {
			naveDirecao.setX(1);
		}
		
		if(evt.getKeyCode() == KeyEvent.VK_UP) {
			naveDirecao.setY(-0.5f);
		}
		
		if(evt.getKeyCode() == KeyEvent.VK_DOWN) {
			naveDirecao.setY(0.5f);
		}
		
		if(evt.getKeyCode() == KeyEvent.VK_ESCAPE) {
			System.exit(0);	
		}
		
		if(evt.getKeyCode() == KeyEvent.VK_P) {
			ma.getControlaJogo().setPausado(!ma.getControlaJogo().isPausado());
		}
	}
	
	/**
	 * Captura teclas quando forem soltas.
	 */
	public void keyReleased(KeyEvent evt) {
		if(evt.getKeyCode() == KeyEvent.VK_CONTROL) {
			this.tiroPressionado = false;
		}
		if(evt.getKeyCode() == KeyEvent.VK_LEFT) {
			if (naveDirecao.getX() == -1) {
				naveDirecao.setX(0);
			}
		}
		
		if(evt.getKeyCode() == KeyEvent.VK_RIGHT) {
			if (naveDirecao.getX() == 1) {
				naveDirecao.setX(0);
			}
		}
		
		if(evt.getKeyCode() == KeyEvent.VK_UP) {
			if (naveDirecao.getY() == -0.5f) {
				naveDirecao.setY(0);
			}
		}
		
		if(evt.getKeyCode() == KeyEvent.VK_DOWN) {
			if (naveDirecao.getY() == 0.5f) {
				naveDirecao.setY(0);
			}
		}
	}
	
	/**
	 * Placehold necessario para o funcionamento da classe.
	 */
	public void keyTyped(KeyEvent evt) {
		// Manter vazia.
	}
	
	/**
	 * @return Retorna o o tempo (em milissegundos) do ultimo frame.
	 */
	public long getTempoUltimoFrame() {
		return tempoUltimoFrame;
	}
	
	/**
	 * Diz a Thread que ela dever� finalizar no pr�ximo loop.
	 *
	 */
	public void stopThread(){
		this.stopThread = true;
	}	
}
